#include "json.hpp"
#include "parser.hpp"
#include <sstream>
#include <stdexcept>

using namespace std;
using namespace imitation::json;

Json::Json() : json_type(json_null)
{
    inner = std::monostate();
}
Json::Json(const bool value) : json_type(json_bool)
{
    inner = value;
}
Json::Json(const int value) : json_type(json_int)
{
    inner = value;
}
Json::Json(const double value) : json_type(json_double)
{
    inner = value;
}
Json::Json(const char * value) : json_type(json_string)
{
    inner = std::string(value);
}
Json::Json(const std::string & value) : json_type(json_string)
{
    inner = value;
}
Json::Json(const std::vector<Json> & value) : json_type(json_array)
{
    inner = value;
}
Json::Json(const std::map<std::string, Json> & value) : json_type(json_object)
{
    inner = value;
}
Json::Json(Type type) : json_type(type)
{
    switch (type)
    {
    case json_null:
        inner = std::monostate();
        break;
    case json_bool:
        inner = false;
        break;
    case json_int:
        inner = 0;
        break;
    case json_double:
        inner = 0.0;
        break;
    case json_string:
        inner = std::string();
        break;
    case json_array:
        inner = std::vector<Json>();
        break;
    case json_object:
        inner = std::map<std::string, Json>();
        break;
    }
}
Json::Json(const Json & value) : json_type(value.json_type)
{
    inner = value.inner;
}
Json & Json::operator=(const Json & value)
{
    json_type = value.json_type;
    inner = value.inner;
    return *this;
}

Json::operator bool() const
{
    return std::get<bool>(inner);
}
Json::operator int() const
{
    return std::get<int>(inner);
}
Json::operator double() const
{
    return std::get<double>(inner);
}
Json::operator std::string() const
{
    return std::get<std::string>(inner);
}

bool Json::operator==(const Json &value) const
{
    if(json_type != value.json_type){
        return false;
    }
    switch (json_type)
    {
    case json_null:
        return std::get<std::monostate>(inner) == std::get<std::monostate>(value.inner);
        break;
    case json_bool:
        return std::get<bool>(inner) == std::get<bool>(value.inner);
        break;
    case json_int:
        return std::get<int>(inner) == std::get<int>(value.inner);
        break;
    case json_double:
        return std::get<double>(inner) == std::get<double>(value.inner);
        break;
    case json_string:
        return std::get<std::string>(inner) == std::get<std::string>(value.inner);
        break;
    case json_array:
        return std::get<std::vector<Json>>(inner) == std::get<std::vector<Json>>(value.inner);
        break;
    case json_object:
        return std::get<std::map<std::string, Json>>(inner) == std::get<std::map<std::string, Json>>(value.inner);
        break;
    default:
        return false;
        break;
    }
}
bool Json::operator!=(const Json &value) const
{
    return !(*this == value);
}

bool Json::is_null() const
{
    return json_type == json_null;
}
bool Json::is_bool() const
{
    return json_type == json_bool;
}
bool Json::is_int() const
{
    return json_type == json_int;
}
bool Json::is_double() const
{
    return json_type == json_double;
}
bool Json::is_string() const
{
    return json_type == json_string;
}
bool Json::is_array() const
{
    return json_type == json_array;
}
bool Json::is_object() const
{
    return json_type == json_object;
}
bool Json::as_bool() const
{
    if(json_type != json_bool){
        throw std::logic_error("not bool");
    }
    else{
        return std::get<bool>(inner);
    }
}
int Json::as_int() const
{
    if(json_type != json_int){
        throw std::logic_error("not int");
    }
    else{
        return std::get<int>(inner);
    }
}
double Json::as_double() const
{
    if(json_type != json_double){
        throw std::logic_error("not double");
    }
    else{
        return std::get<double>(inner);
    }
}
std::string Json::as_string() const
{
    if(json_type != json_string){
        throw std::logic_error("not string");
    }
    else{
        return std::get<std::string>(inner);
    }
}
std::vector<Json> Json::as_array() const
{
    if(json_type != json_array){
        throw std::logic_error("not array");
    }
    else{
        return std::get<std::vector<Json>>(inner);
    }
}
std::map<std::string, Json> Json::as_object() const
{
    if(json_type != json_object){
        throw std::logic_error("not object");
    }
    else{
        return std::get<std::map<std::string, Json>>(inner);
    }
}

bool Json::has(const std::string &key) const
{
    return has(key.c_str());
}
bool Json::has(const char *key) const
{
    if(json_type != json_object){
        return false;
    }
    return std::get<std::map<std::string, Json>>(inner).find(key) != std::get<std::map<std::string, Json>>(inner).end();
}
bool Json::has(int index) const
{
    if(json_type != json_array || index < 0){
        return false;
    }
    return (index < std::get<std::vector<Json>>(inner).size());
}

void Json::remove(const std::string &key)
{
    return remove(key.c_str());
}
void Json::remove(const char *key)
{
    if(json_type != json_object){
        return;
    }
    if(!has(key))
        return;
    std::get<std::map<std::string, Json>>(inner).erase(key);
}
void Json::remove(int index)
{
    if(!has(index))
        return;
    std::get<std::vector<Json>>(inner).erase(std::get<std::vector<Json>>(inner).begin()+ index);
}

Json &imitation::json::Json::operator[](int i)
{
    if(i < 0)
        throw std::out_of_range("out of range");
    if(json_type != json_array){
        json_type = json_array;
        inner.emplace<std::vector<Json>>(); //一定要修改inner类型
    }
    if(i >= std::get<std::vector<Json>>(inner).size()){
        std::get<std::vector<Json>>(inner).resize(i + 1);
    }
    return std::get<std::vector<Json>>(inner)[i];
}
void imitation::json::Json::append(const Json &value)
{
    if(json_type != json_array){
        json_type = json_array;
        inner.emplace<std::vector<Json>>();
    }
    std::get<std::vector<Json>>(inner).push_back(value);
}

std::string imitation::json::Json::str() const
{
    ostringstream ss;
    switch (json_type)
    {
    case json_null:
        ss << "";
        break;
    case json_bool:
        if(std::get<bool>(inner))
            ss << "true";
        else
            ss << "false";
        break;
    case json_int:
        ss << std::get<int>(inner);
        break;
    case json_double:
        ss << std::get<double>(inner);  //ss模块有精度限制
        break;
    case json_string:
        ss << "\"" << std::get<std::string>(inner) << "\"";
        break;
    case json_array:
        ss << "[";
        for(auto iter = std::get<std::vector<Json>>(inner).begin(); iter != std::get<std::vector<Json>>(inner).end(); iter++){
            if(iter != std::get<std::vector<Json>>(inner).begin()){
                ss << ",";
            }
            ss << iter->str();
        }
        ss << "]";
        break;
    case json_object:
        ss << "{";
        for(auto iter = std::get<std::map<std::string, Json>>(inner).begin(); iter != std::get<std::map<std::string, Json>>(inner).end(); iter++){
            if(iter != std::get<std::map<std::string, Json>>(inner).begin()){
                ss << ",";
            }
            ss << "\"" << iter->first << "\":" << iter->second.str();
        }
        ss << "}";
        break;
    default:
        break;
    }
    return ss.str();
}

Json &imitation::json::Json::operator[](const std::string &key)
{
    if (json_type != json_object)
    {
        json_type = json_object;
        inner.emplace<std::map<std::string, Json>>();
    }
    return std::get<std::map<std::string, Json>>(inner)[key];
}

Json &imitation::json::Json::operator[](const char *key)
{
    return (*this)[std::string(key)];
}

void imitation::json::Json::parse(const std::string &json)
{
    Parser p;
    p.load(json);
    *this = p.parse();
}
